(function() {
  var __hasProp = {}.hasOwnProperty,
    __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; };

  if (!window.Moshu) {
    window.Moshu = new Object();
  }

  window.Moshu.Canvas2D = new Object();

  Moshu.Canvas2D.Vector = (function() {

    function Vector(x, y) {
      this.arr = [x, y];
      this.isVector = true;
    }

    Vector.prototype.x = function(x) {
      if (x) {
        this.arr[0] = x;
      }
      return this.arr[0];
    };

    Vector.prototype.y = function(y) {
      if (y) {
        this.arr[1] = y;
      }
      return this.arr[1];
    };

    Vector.prototype.substract = function(vector) {
      return new Vector(this.x() - vector.x(), this.y() - vector.y());
    };

    Vector.prototype.add = function(vector) {
      return new Vector(this.x() + vector.x(), this.y() + vector.y());
    };

    Vector.prototype.dotProduct = function(vector) {
      return this.x() * vector.x() + this.y() * vector.y();
    };

    Vector.prototype.getPerpendicular = function() {
      return new Vector(this.y(), -this.x());
    };

    Vector.prototype.getMagnitude = function() {
      return Math.sqrt(this.x() * this.x() + this.y() * this.y());
    };

    return Vector;

  })();

  window.Moshu.Canvas2D.PhysicalObject = (function(_super) {

    __extends(PhysicalObject, _super);

    function PhysicalObject() {
      PhysicalObject.__super__.constructor.apply(this, arguments);
      this.mass = 0;
      this.elasticity = 0;
      this.mediumFrictionCoef = 0;
      this.objectFrictionCoef = 0;
      this._velocity = new Moshu.Canvas2D.Vector(0, 0);
      this._acceleration = new Moshu.Canvas2D.Vector(0, 0);
      this.fixed = false;
      this.isPhysicalObject = true;
      this.isSolid = false;
    }

    PhysicalObject.prototype.velocity = function(val) {
      if (val) {
        if (!this.fixed) {
          if (val.isVector) {
            this._velocity = val;
          } else {
            this.log("The parameter needs to be a Vector. (Moshu.Canvas2D.Vector object)");
          }
        } else {
          this.log("Cannot change the velocity of a fixed object. Try setting fixed attribute to false first.");
        }
      }
      return this._velocity;
    };

    PhysicalObject.prototype.acceleration = function(val) {
      if (val) {
        if (!this.fixed) {
          if (val.isVector) {
            this._acceleration = val;
          } else {
            this.log("The parameter needs to be a Vector. (Moshu.Canvas2D.Vector object)");
          }
        } else {
          this.log("Cannot change the acceleration of a fixed object. Try setting fixed attribute to false first.");
        }
      }
      return this._acceleration;
    };

    PhysicalObject.prototype.getMomentum = function() {
      return new Moshu.Canvas2D.Vector(this.mass * this._velocity.x(), this.mass * this._velocity.y());
    };

    return PhysicalObject;

  })(Moshu.Observable);

  window.Moshu.Canvas2D.PositionedObject = (function(_super) {

    __extends(PositionedObject, _super);

    function PositionedObject() {
      PositionedObject.__super__.constructor.apply(this, arguments);
      this.isPositionedObject = true;
    }

    PositionedObject.prototype.move = function(vector) {
      throw "Not implemented";
    };

    PositionedObject.prototype.getCollision = function(obj) {
      throw "Not implemented";
    };

    PositionedObject.prototype.manageCollision = function(obj) {
      throw "Not implemented";
    };

    return PositionedObject;

  })(Moshu.Canvas2D.PhysicalObject);

  window.Moshu.Canvas2D.Polygon = (function(_super) {

    __extends(Polygon, _super);

    function Polygon(vertices) {
      var vertex, _i, _len;
      Polygon.__super__.constructor.apply(this, arguments);
      this.isPolygon = true;
      this._vertices = [];
      if (vertices && vertices.length) {
        for (_i = 0, _len = vertices.length; _i < _len; _i++) {
          vertex = vertices[_i];
          if (vertex && vertex.isVector) {
            this._vertices.push(vertex);
          } else {
            this.log("Vertex is undefined or is not a vector. Vertex ignored...");
          }
        }
      } else {
        this.log("Provided vertices is not an array. You need to supply an array.");
      }
      this._savedVertices = null;
    }

    Polygon.prototype.save = function() {
      var v, _i, _len, _ref, _results;
      this._savedVertices = [];
      _ref = this._vertices;
      _results = [];
      for (_i = 0, _len = _ref.length; _i < _len; _i++) {
        v = _ref[_i];
        _results.push(this._savedVertices.push(new Moshu.Canvas2D.Vector(v.x(), v.y())));
      }
      return _results;
    };

    Polygon.prototype.restore = function() {
      var v, _i, _len, _ref, _results;
      if (this._savedVertices) {
        this._vertices = [];
        _ref = this._savedVertices;
        _results = [];
        for (_i = 0, _len = _ref.length; _i < _len; _i++) {
          v = _ref[_i];
          _results.push(this._vertices.push(new Moshu.Canvas2D.Vector(v.x(), v.y())));
        }
        return _results;
      }
    };

    Polygon.prototype.getVertices = function() {
      return this._vertices;
    };

    Polygon.prototype.move = function(vector) {
      var i, v, _i, _len, _ref, _results;
      if (vector && vector.isVector) {
        if (vector.x() !== 0 || vector.y() !== 0) {
          _ref = this._vertices;
          _results = [];
          for (i = _i = 0, _len = _ref.length; _i < _len; i = ++_i) {
            v = _ref[i];
            _results.push(this._vertices[i] = v.add(vector));
          }
          return _results;
        }
      } else {
        return this.log("Supplied argument of move command is not a vector.");
      }
    };

    Polygon.prototype.rotate = function(angle, rotationCenter) {
      var centerX, centerY, cosAngle, dx, dy, sinAngle, vertex, _i, _len, _ref, _results;
      if (angle && angle !== 0) {
        centerX = rotationCenter.x();
        centerY = rotationCenter.y();
        if (centerX && centerY) {
          cosAngle = Math.cos(angle);
          sinAngle = Math.sin(angle);
          _ref = this._vertices;
          _results = [];
          for (_i = 0, _len = _ref.length; _i < _len; _i++) {
            vertex = _ref[_i];
            dx = vertex.x() - centerX;
            dy = vertex.y() - centerY;
            vertex.x(centerX + dx * cosAngle - dy * sinAngle);
            _results.push(vertex.y(centerY + dx * sinAngle + dy * cosAngle));
          }
          return _results;
        }
      }
    };

    Polygon.prototype.getSidesVectors = function() {
      var arr, i, sideVector, v, v1, v2, _i, _len, _ref;
      arr = [];
      _ref = this._vertices;
      for (i = _i = 0, _len = _ref.length; _i < _len; i = ++_i) {
        v = _ref[i];
        v1 = this._vertices[i];
        v2 = null;
        if (i === this._vertices.length - 1) {
          v2 = this._vertices[0];
        } else {
          v2 = this._vertices[i + 1];
        }
        sideVector = v1.substract(v2);
        arr.push(sideVector);
      }
      return arr;
    };

    Polygon.prototype.getProjection = function(axis) {
      var i, max, min, v, val, vertices, _i, _len;
      vertices = this._vertices;
      min = axis.dotProduct(vertices[0]);
      max = min;
      for (i = _i = 0, _len = vertices.length; _i < _len; i = ++_i) {
        v = vertices[i];
        val = axis.dotProduct(v);
        if (min > val) {
          min = val;
        }
        if (max < val) {
          max = val;
        }
      }
      return [min, max];
    };

    Polygon.prototype.getCollision = function(polygon) {
      var axis, collision, free, mag, overlap, p1, p2, side, sides1, sides2, _i, _j, _len, _len1;
      if (polygon.isPolygon) {
        sides1 = this.getSidesVectors();
        sides2 = polygon.getSidesVectors();
        free = false;
        collision = {
          collider: this,
          collided: polygon,
          recoveryVector: null,
          recoveryMagnitude: null
        };
        for (_i = 0, _len = sides1.length; _i < _len; _i++) {
          side = sides1[_i];
          axis = side.getPerpendicular();
          p1 = this.getProjection(axis);
          p2 = polygon.getProjection(axis);
          overlap = false;
          mag = 0;
          if (p1[0] > p2[0] && p1[0] < p2[1]) {
            overlap = true;
            mag = p2[1] - p1[0];
          }
          if (p1[1] > p2[0] && p1[1] < p2[1]) {
            overlap = true;
            mag = p2[1] - p1[0];
          }
          if (p2[0] > p1[0] && p2[0] < p1[1]) {
            overlap = true;
            mag = p1[1] - p2[0];
          }
          if (p2[1] > p1[0] && p2[1] < p1[1]) {
            overlap = true;
            mag = p1[1] - p2[0];
          }
          if (p1[0] === p2[0] && p1[1] === p2[1]) {
            overlap = true;
            mag = p1[1] - p2[0];
          }
          if (!overlap) {
            free = true;
          } else {
            if (mag < collision.recoveryMagnitude || !collision.recoveryMagnitude) {
              collision.recoveryMagnitude = mag;
              collision.recoveryVector = axis;
            }
          }
        }
        for (_j = 0, _len1 = sides2.length; _j < _len1; _j++) {
          side = sides2[_j];
          axis = side.getPerpendicular();
          p1 = this.getProjection(axis);
          p2 = polygon.getProjection(axis);
          overlap = false;
          mag = 0;
          if (p1[0] >= p2[0] && p1[0] <= p2[1]) {
            overlap = true;
            mag = p2[1] - p1[0];
          }
          if (p2[0] >= p1[0] && p2[0] <= p1[1]) {
            overlap = true;
            mag = p1[1] - p2[0];
          }
          if (!overlap) {
            free = true;
          } else {
            if (mag < collision.recoveryMagnitude || !collision.recoveryMagnitude) {
              collision.recoveryMagnitude = mag;
              collision.recoveryVector = axis;
            }
          }
        }
        if (free) {
          return null;
        } else {
          if (collision.recoveryVector) {
            collision.recoveryMagnitude = collision.recoveryMagnitude / collision.recoveryVector.getMagnitude();
          }
          return collision;
        }
      } else {
        this.log("getCollision requires a Polygon attribute.");
        return null;
      }
    };

    Polygon.prototype.getBoundary = function() {
      var elem, maxx, maxy, minx, miny, _i, _len, _ref;
      if (this._vertices && this._vertices.length) {
        minx = null;
        maxx = null;
        miny = null;
        maxy = null;
        _ref = this._vertices;
        for (_i = 0, _len = _ref.length; _i < _len; _i++) {
          elem = _ref[_i];
          if (elem.x() < minx || !minx) {
            minx = elem.x();
          }
          if (elem.x() > maxx || !maxx) {
            maxx = elem.x();
          }
          if (elem.y() < miny || !miny) {
            miny = elem.y();
          }
          if (elem.y() > maxy || !maxy) {
            maxy = elem.y();
          }
        }
        return [minx, miny, maxx, maxy];
      } else {
        return [0, 0, 0, 0];
      }
    };

    return Polygon;

  })(Moshu.Canvas2D.PositionedObject);

  window.Moshu.Canvas2D.RegularPolygon = (function(_super) {

    __extends(RegularPolygon, _super);

    function RegularPolygon(x, y, radius, rank) {
      var i, vertex, vertices, _i, _ref;
      vertices = [];
      for (i = _i = 0, _ref = rank - 1; _i <= _ref; i = _i += 1) {
        x = (Math.sin(i / rank * 2 * Math.PI) * radius) + x;
        y = (Math.cos(i / rank * 2 * Math.PI) * radius) + y;
        vertex = new Moshu.Canvas2D.Vector(x, y);
        vertices.push(vertex);
      }
      RegularPolygon.__super__.constructor.call(this, vertices);
    }

    return RegularPolygon;

  })(Moshu.Canvas2D.Polygon);

  window.Moshu.Canvas2D.Line = (function(_super) {

    __extends(Line, _super);

    function Line(x, y, width, height) {
      Line.__super__.constructor.call(this, [[x, y], [x + width, y + height]]);
      this.isClosed = false;
    }

    return Line;

  })(Moshu.Canvas2D.Polygon);

  window.Moshu.Canvas2D.Triangle = (function(_super) {

    __extends(Triangle, _super);

    function Triangle(x1, y1, x2, y2, x3, y3) {
      Triangle.__super__.constructor.call(this, [[x1, y1], [x2, y2], [x3, y3]]);
    }

    return Triangle;

  })(Moshu.Canvas2D.Polygon);

  window.Moshu.Canvas2D.Rectangle = (function(_super) {

    __extends(Rectangle, _super);

    function Rectangle(x, y, width, height) {
      this.x = x;
      this.y = y;
      this.width = width;
      this.height = height;
      Rectangle.__super__.constructor.call(this, [new Moshu.Canvas2D.Vector(this.x, this.y), new Moshu.Canvas2D.Vector(this.x + this.width, this.y), new Moshu.Canvas2D.Vector(this.x + this.width, this.y + this.height), new Moshu.Canvas2D.Vector(this.x, this.y + this.height)]);
    }

    return Rectangle;

  })(Moshu.Canvas2D.Polygon);

  window.Moshu.Canvas2D.CanvasImage = (function(_super) {

    __extends(CanvasImage, _super);

    function CanvasImage(src, x, y, width, height) {
      this.src = src;
      this.x = x;
      this.y = y;
      this.width = width;
      this.height = height;
      this.isImage = true;
      CanvasImage.__super__.constructor.call(this, this.x, this.y, this.width, this.height);
      this.image = new Image();
      this.image.src = this.src;
    }

    CanvasImage.prototype.recomputePosition = function() {
      var i, minx, miny, v, vertices, _i, _len;
      vertices = this._vertices;
      minx = vertices[0].x();
      miny = vertices[0].y();
      for (i = _i = 0, _len = vertices.length; _i < _len; i = ++_i) {
        v = vertices[i];
        if (v.x() < minx) {
          minx = v.x();
        }
        if (v.y() < miny) {
          miny = v.y();
        }
      }
      this.x = minx;
      return this.y = miny;
    };

    CanvasImage.prototype.rotate = function(angle, rotationCenter) {
      CanvasImage.__super__.rotate.call(this, angle, rotationCenter);
      return this.recomputePosition();
    };

    CanvasImage.prototype.move = function(vector) {
      CanvasImage.__super__.move.call(this, vector);
      return this.recomputePosition();
    };

    CanvasImage.prototype.save = function() {
      CanvasImage.__super__.save.call(this);
      return this.recomputePosition();
    };

    CanvasImage.prototype.restore = function() {
      CanvasImage.__super__.restore.call(this);
      return this.recomputePosition();
    };

    return CanvasImage;

  })(Moshu.Canvas2D.Rectangle);

  window.Moshu.Canvas2D.Painter = (function() {

    function Painter() {
      this.isPainter = true;
    }

    return Painter;

  })();

  window.Moshu.Canvas2D.PolygonPainter = (function(_super) {

    __extends(PolygonPainter, _super);

    function PolygonPainter(context, polygon) {
      this.context = context;
      this.polygon = polygon;
      PolygonPainter.__super__.constructor.apply(this, arguments);
      this.fill = {
        color: "#FFF",
        enabled: false
      };
      this.stroke = {
        width: 1,
        color: "#000",
        enabled: true
      };
    }

    PolygonPainter.prototype.draw = function() {
      var ctx, i, vertex, vertices, _i, _len;
      ctx = this.context;
      if (this.stroke.enabled) {
        ctx.lineWidth = this.stroke.width;
        ctx.strokeStyle = this.stroke.color;
      } else {
        ctx.lineWidth = 0;
      }
      if (this.fill.enabled) {
        ctx.fillStyle = this.fill.color;
      } else {
        ctx.fillStyle = null;
      }
      ctx.beginPath();
      vertices = this.polygon.getVertices();
      for (i = _i = 0, _len = vertices.length; _i < _len; i = ++_i) {
        vertex = vertices[i];
        if (i === 0) {
          ctx.moveTo(vertex.x(), vertex.y());
        } else {
          ctx.lineTo(vertex.x(), vertex.y());
        }
      }
      ctx.closePath();
      if (this.stroke.enabled) {
        ctx.stroke();
      }
      if (this.fill.enabled) {
        return ctx.fill();
      }
    };

    return PolygonPainter;

  })(Moshu.Canvas2D.Painter);

  window.Moshu.Canvas2D.ImagePainter = (function(_super) {

    __extends(ImagePainter, _super);

    function ImagePainter(context, image) {
      this.context = context;
      this.image = image;
      ImagePainter.__super__.constructor.apply(this, arguments);
    }

    ImagePainter.prototype.draw = function() {
      var ctx;
      ctx = this.context;
      return ctx.drawImage(this.image.image, this.image.x, this.image.y, this.image.width, this.image.height);
    };

    return ImagePainter;

  })(Moshu.Canvas2D.Painter);

  window.Moshu.Canvas2D.World = (function(_super) {

    __extends(World, _super);

    function World(id) {
      this.id = id;
      World.__super__.constructor.apply(this, arguments);
      this.canvas = document.getElementById(this.id);
      if (this.canvas) {
        this.context = this.canvas.getContext("2d");
        this.width = this.canvas.width;
        this.height = this.canvas.height;
      } else {
        this.context = null;
      }
      if (!this.context) {
        throw "Invalid attribute supplied to the World object. Canvas could not be found or accessed.";
      }
      this.shapes = [];
      window.Moshu.AnimationManager.bind(this, this.timeUpdate);
      this.lastUpdate = null;
      this.frozen = false;
    }

    World.prototype.createShape = function(physicalObject, objectPainter) {
      if (physicalObject && objectPainter && physicalObject.isPhysicalObject && objectPainter.isPainter) {
        physicalObject.painter = objectPainter;
        return this.shapes.push(physicalObject);
      } else {
        return this.logError("Invalid attributes in createShape function. The first argument must be a PhysicalObject, and the second must be a Painter.");
      }
    };

    World.prototype.createShapeFromPolygon = function(polygon) {
      if (polygon && polygon.isPolygon) {
        return this.createShape(polygon, new Moshu.Canvas2D.PolygonPainter(this.context, polygon));
      } else {
        return this.logError("Invalid attributes in createShapeFromPolygon function. The first argument must be a Polygon, and the second must be a Painter.");
      }
    };

    World.prototype.createShapeFromImage = function(image) {
      if (image && image.isImage) {
        return this.createShape(image, new Moshu.Canvas2D.ImagePainter(this.context, image));
      } else {
        return this.logError("Invalid attributes in createShapeFromImage function. The first argument must be an Image, and the second must be a Painter.");
      }
    };

    World.prototype.remove = function(shape) {
      var i, s, _i, _len, _ref, _results;
      _ref = this.shapes;
      _results = [];
      for (i = _i = 0, _len = _ref.length; _i < _len; i = ++_i) {
        s = _ref[i];
        if (s === shape) {
          this.shapes.splice(i, 1);
          break;
        } else {
          _results.push(void 0);
        }
      }
      return _results;
    };

    World.prototype.clear = function() {
      return this.shapes = [];
    };

    World.prototype.freeze = function() {
      this.fronzen = true;
      return this.frozen;
    };

    World.prototype.unfreeze = function() {
      this.fronzen = false;
      return this.frozen;
    };

    World.prototype.refresh = function() {
      var shape, _i, _len, _ref, _results;
      if (!this.frozen) {
        this.context.clearRect(0, 0, this.width, this.height);
        _ref = this.shapes;
        _results = [];
        for (_i = 0, _len = _ref.length; _i < _len; _i++) {
          shape = _ref[_i];
          if (shape && shape.painter) {
            _results.push(shape.painter.draw());
          } else {
            _results.push(void 0);
          }
        }
        return _results;
      }
    };

    World.prototype.supressTimer = function() {
      return window.Moshu.AnimationManager.unbind(this.timeUpdate);
    };

    World.prototype.timeUpdate = function(dt) {
      var c, distance, s, shape, velocityIncreaseVector, _i, _j, _len, _len1, _ref, _ref1;
      _ref = this.shapes;
      for (_i = 0, _len = _ref.length; _i < _len; _i++) {
        shape = _ref[_i];
        if (shape && !shape.fixed) {
          dt = dt / 1000;
          velocityIncreaseVector = new Moshu.Canvas2D.Vector(shape.acceleration().x() * dt, shape.acceleration().y() * dt);
          shape.velocity(shape.velocity().add(velocityIncreaseVector));
          distance = new Moshu.Canvas2D.Vector(shape.velocity().x() * dt, shape.velocity().y());
          shape.move(distance);
          _ref1 = this.shapes;
          for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) {
            s = _ref1[_j];
            if (!(s === shape)) {
              c = shape.getCollision(s);
              if (c) {
                shape.manageCollision(c);
              }
            }
          }
        }
      }
      return this.refresh();
    };

    return World;

  })(Moshu.Observable);

}).call(this);
